#include "Graphics/Matrix.hpp"
#include <memory.h>
#include <iostream>
#include <math.h>

using std::cout;
using std::cerr;
using std::endl;

using Graph::Matrix;

bool equal(const Matrix& m1, const Matrix& m2, const double eps)
{
    const double * data1 = m1.data(), * data2 = m2.data();
    for (int32_t i = Matrix::ELEMENTS_COUNT - 1; i >= 0; --i)
        if (fabs(data1[i] != data2[i]) > eps)
            return false;
    return true;
}

bool testAccess(const Matrix& m)
{
    const double * data = m.data();
    for (int32_t i = 0; i < Matrix::ROWS_COUNT; ++i)
        for (int32_t j = 0; j < Matrix::COLS_COUNT; ++j)
            if (m[i][j] != data[i * Matrix::COLS_COUNT +j])
                return false;
    return true;
}

int main(int argc, char * argv[])
{
    int exit_code = 0;

    const double EPS = 1.0e-15;

    const double data1[] = {1.0, 2.0, 3.0, 4.0,
                            5.0, 6.0, 7.0, 8.0,
                            9.0, 10.0, 11.0, 12.0,
                            13.0, 14.0, 15.0, 16.0};

    const double data2[] = {1.0, 0.0, 0.0, 0.0,
                            0.0, 1.0, 0.0, 0.0,
                            0.0, 0.0, 1.0, 0.0,
                            0.0, 0.0, 0.0, 1.0};

    const double data3[] = {2.0, 4.0, 3.0, 7.0,
                            6.0, 5.0, 10.0, 12.0,
                            15.0, 16.0, 9.0, 1.0,
                            8.0, 11.0, 13.0, 12.0};

    const double resd1[] = {3.0, 6.0, 6.0, 11.0,
                            11.0, 11.0, 17.0, 20.0,
                            24.0, 26.0, 20.0, 13.0,
                            21.0, 25.0, 28.0, 28.0};

    const double resd2[] = {-1.0, -2.0, 0.0, -3.0,
                            -1.0, 1.0, -3.0, -4.0,
                            -6.0, -6.0, 2.0, 11.0,
                             5.0, 3.0, 2.0, 4.0};

    const double resd3[] = {91.0, 106.0, 102.0, 82.0,
                            215.0, 250.0, 242.0, 210.0,
                            339.0, 394.0, 382.0, 338.0,
                            463.0, 538.0, 522.0, 466.0};

    Matrix m1, m2, m3;
    ::memcpy(m1.data(), data1, sizeof(data1));
    ::memcpy(m2.data(), data2, sizeof(data2));
    ::memcpy(m3.data(), data3, sizeof(data3));

    if (m1 != m1 || m2 != m2 || m3 != m3)
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }
    if (m1 == m2 || m1 == m3 || m2 == m3)
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }
    if (!equal(m1, m1 * m2, EPS))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }
    if (!equal(-1.0 * m1, -m1, EPS))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }

    Matrix r1, r2, r3;
    ::memcpy(r1.data(), resd1, sizeof(resd1));
    ::memcpy(r2.data(), resd2, sizeof(resd2));
    ::memcpy(r3.data(), resd3, sizeof(resd3));

    if (!equal(r1, m1 + m3, EPS))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }
    if (!equal(r2, m1 - m3, EPS))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }
    if (!equal(r3, m1 * m3, EPS))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }

    if (!testAccess(m1))
    {
        cerr << "Test failed " << __FILE__ << " line " << __LINE__ - 2 << endl;
        exit_code = 1;
    }

    return exit_code;
}
